;
; vmdev.asm
;

include vmbdoor.inc

; enable x486 instructions, but use 16 bit segment
.486
option segment:use16

; code segment
codes	segment para use16 public 'CODE'
codes	ends

; data segment
datas	segment byte use16 public 'DATA'
BDOOR_MAGIC	dword  	VMWARE_MAGIC
BDOOR_PORT  	word	VMWARE_PORT
CONNECTED	byte	'  + ', TAB, '$'
DISCONNECTED	byte	'    ', TAB, '$'
MSG_USAGE	byte	\
'Usage: vmdev [<action>][<target>]', CR, LF, CR, LF,\
'<action>', CR, LF, \
'  +', TAB, '(Re)connect target device(s). This is the default.', CR, LF, \
'  -', TAB, 'Disconnect target device(s).', CR, LF, CR, LF
MSG_USAGE2	byte	\
'<target>', CR, LF, \
'  0..9', TAB, 'Target device number. ', CR, LF, \
'  *', TAB, 'Represents all devices. This is the default.', CR, LF, CR, LF
MSG_USAGE3	byte	\
'When both <action> and <target> options are omitted, ', CR, LF, \
'current device state list is shown.', CR, LF, \
'Connected devices are marked with "+" in the list.', CR, LF, '$'
MSG_VMNG	byte	'Not running inside VMware.'
END_OF_LINE	byte	CR, LF, '$'
datas	ends

MSG_DEVLIST	= MSG_USAGE			; these are never used together

main	group codes,datas

codes	segment
; for tiny memory model (.com), all seg is the same
assume	cs:main,ds:main,es:main,ss:main
org	100h
entry:
check_vm	proc near
	; Check if we are running inside VMware
	xor 	ecx,ecx
	mov 	cl, VMCMD_GET_VERSION
        call	backdoor

	cmp	ebx,[BDOOR_MAGIC]
	jne	not_in_vm
	; this procedure doesn't return
	; continue to the next procedure
check_vm	endp

;************************************************
; Process command line
;************************************************
command_line	proc near
	mov	cl, byte ptr [80h]		; command line length
	mov	si, byte ptr [81h]		; command line top

	mov	dl, 0ffh			; dl = device num (default:all)
	mov	bx, 0ff80h			; bh = list (default:yes)
						; bl = connect (default:yes)
next_char:
	dec	cl				; end of command line?
	jz	dispatch			; yes. do the actual operation

	cld					; clear direction flag so that
						; lodsb will increment SI
	lodsb					; mov al,[ds:si] & inc si

	cmp	al, CR				; CR character?
	je	dispatch			; yes. end of command line

	cmp	al, ' '				; blank?
	je	next_char			; yes. get next char

	cmp	al, TAB				; tab?
	je	next_char			; yes. get next char

	cmp	al, '-'				; minus?
	je	set_disconnect			; yes. set disconnect flag

	cmp	al, '+'				; plus?
	je	set_connect			; yes. clear disconnect flag

	cmp	al, '*'				; asterisk?
	je	set_alldev			; yes. choose 'all' devices

	cmp	al, '0'				; below '0'?
	jb	usage				; yes. invalid option

	cmp	al, '9'				; above '9'
	ja	usage				; yes. invalid option

	; now correct device number is specified
	xor	bh, bh				; clear 'list' flag
						; 'connect' flag remains as set
						; by +/- character or default.
	mov	dl, al				; set device number
	sub	dl, 30h				; convert numeric char to value
	jmp	dispatch			; do the actual operation

set_disconnect:					; '-' in command line
	xor	bx, bx				; clear 'list', clear 'connect'
	jmp	next_char			; get next char

set_connect:					; '+' in command line
	xor	bh, bh				; clear 'list' flag
						; 'connect' remains as default
	jmp	next_char			; get next char

set_alldev:					; '*'  in command line
	xor	bh, bh				; clear 'list' flag
						; 'connect' flag remains as set
						; by +/- character or default.
	; fall to dispatch procedure
	; for no more parsing is needed
command_line	endp

;************************************************
; dispatch operation based on command line
;************************************************
dispatch	proc near
	and	bh, bh				; 'list' flag clear?
	jnz	list_devices			; no. show device list

	cmp	dl, 0ffh			; all devices?
	je	all_devices			; yes. treat all devices

	; now deal with only one device
	call	one_device
	jmp	list_devices			; show latest state list
dispatch	endp

;************************************************
; terminate routines
;************************************************
usage:
	mov	dx, offset MSG_USAGE		; usage message
	jmp	msg_and_exit

not_in_vm:
	mov	dx, offset MSG_VMNG		; not in VM message

msg_and_exit:					; print error message and
						; terminates with status code 0
	mov 	ah, 09h				; print function 
	int	21h				; DOS function call
	xor	al, al				; return code = 0

terminate:					; Now Return to DOS
	mov	ah, 4ch
	int	21h
	; this function call never returnes

;************************************************
; executes vmware backdoor i/o
;************************************************
backdoor	proc near
        mov	eax, [BDOOR_MAGIC]
        mov	dx, [BDOOR_PORT]
        in	eax, dx
	ret
backdoor	endp

;************************************************
; change state of a single device
;************************************************
; IN	bl	connect flag
;	dl	device number
; OUT	eax	result of set_device
one_device	proc near
	push	bx				; store connect flag
	push	dx				; store device number

	xor	ebx, ebx			; clear ebx
	mov	bl, dl				; device number to L.W. of ebx
	call	set_device			; first disconnect anyway

	pop	dx
	pop	bx

	and	bl, bl				; connect flag clear?
	jz	exit_proc			; yes. no more job

	bswap	ebx				; move connect flag to H.W.
	movzx	bx, dl				; set device number to L.W.
	call	set_device			; connect again

exit_proc:
	ret
one_device	endp

;************************************************
; change state of all devices present
;************************************************
; IN	bl	connect flag
all_devices	proc near
	xor	dx, dx				; start from device 0
next_device:
	push	dx				; store device number
	push	bx				; store connect flag

	call	one_device			; treat one device

	pop	bx				; connect flag
	pop	dx				; device number
	inc	dx				; next device number

	cmp	al, 1				; result of set_device
	je	next_device			; ok. do next device

	; do not jump or anything
	; show latest state list
all_devices	endp

;************************************************
; List all present devices
;************************************************
list_devices	proc near
	xor	bx, bx				; start from device 0
next_device:
	push	bx
	call	get_device			; get device info
	pop	bx

	cmp	al, 1				; test status
	jne	terminate			; get_device failed..

	mov	di, offset CONNECTED		; header for connected item

	mov	al, [MSG_DEVLIST + 36]		; connect indicator byte
	and	al, al				; disconnected?
	jnz	set_devnum			; no. connected

	add	di, 6				; header for disconnected item

set_devnum:
	mov	al, bl
	add	al, 30h				; convert to numeric character
	mov	[di + 3], al			; put it in the right place

	inc	bx				; next device number

	mov	dx, di				; string address
	mov 	ah, 09h				; print function
	int	21h				; DOS function call

	mov	si, offset MSG_DEVLIST		; top address of device info
next_char:
	cld
	lodsb					; mov al, si & inc si
	and	al, al				; end of device name?
	jnz	next_char			; no. get next character.

	mov	eax, dword ptr [END_OF_LINE]	; add CR,LF,$ sequence
	mov	[si], eax			; at the end of device name

	mov	dx, offset [MSG_DEVLIST]	; string address
	mov	ah, 09h				; print function
	int	21h				; DOS function call

	jmp	next_device			; get next device info
list_devices	endp

;************************************************
; set device state
;************************************************
; ebx (H.W.=state, L.W.=dev #)
set_device	proc near
	; Changing device state before getting device info may fail
	; in certain cases (e.g. right after VMware startup)
	; So get device info first
	push	ebx
	call	get_device
	pop	ebx
	
	cmp	al, 1				; result of get_device
	jne	exit_proc			; failed

	; Now we can safely set device state
	xor	ecx, ecx
	mov	cl, VMCMD_SET_DEVICE
	call	backdoor

exit_proc:
	ret
set_device	endp

;************************************************
; Reads information for one divice (40 bytes)
;************************************************
; IN	bx	device number
; OUT	eax	1:success
;		other:fail
get_device	proc near
	mov	si, offset MSG_DEVLIST		; destination address
	xor	di, di				; data offset

next_data:
	push	bx				; store device number

	shl	ebx, 10h			; shift device num to high word
	mov	bx, di		 		; data offset

	xor	ecx, ecx
	mov	cl, VMCMD_GET_DEVICE
	call	backdoor

	pop	cx				; original device number

	cmp 	al, 1				; check result
	jne 	exit_getdev			; NG

	mov	[si], ebx			; store retrieved data
	mov	bx, cx

	cmp	di, 36				; got all data?
	jae	exit_getdev			; yes. exit procedure

	add	di, 4				; next store position
	add	si, 4				; next data offset
	jmp	next_data

exit_getdev:
	ret
get_device	endp

codes	ends
end	entry
; end of file
